import {
  Button,
  Card,
  Form,
  Input,
  Cascader,
  FormInstance,
  message,
} from "antd";
import React, { useEffect, useRef, useState } from "react";
import { ArrowLeftOutlined } from "@ant-design/icons";
import { useLocation, useNavigate } from "react-router-dom";
import RichTextEditor from "../../components/rich-text-editor";
// 获取分类名称的请求函数reqCategory 如果给一个0就查询一级分类的所有名称
import { reqCategory } from "../../api";
import { addOrUpdateProduct } from "../../api";
import PicWall from "../../components/picWall";

// product的添加和更新的子路由
const UpDate: React.FC = () => {
  // useNavigate()包含实现编程式导航的方法
  const navigate = useNavigate();
  //取出路由携带过来的数据
  // 使用useLocation钩子函数获取跳转到这个页面时候携带的参数
  const location = useLocation();

  // isUpdate用于判断当前页面是用来添加新的商品还是用来修改已有商品的内容
  const [isUpdate, setisUpdate] = useState(false);

  // 用useRef<FormInstance<any>>选择到当前的Form 就可以用ref操作到form上的item
  const Formref = useRef<FormInstance<any>>(null);

  // picRef用于获取子组件照片墙picwall的ref 可以通过ref调用子组件useImperativeHandle暴露的方法
  const picRef = useRef<any>(null);

  // 富文本编辑器的ref
  const richText = useRef<any>(null);

  //imgsArr用来存当前商品修改页面中的图片数组信息
  const [imgsArr, setimgsArr] = useState([]);

  // 初始化富文本编辑器的内容 如果路由中有东西说明是修改商品页面 没有说明是新添加商品
  // 路由有内容就说明是修改 并且把内容搬到富文本编辑器里转换 没有就制空
  const [richDetail, setrichDetail] = useState(
    location.state ? location.state.data.detail : ""
  );

  // const [richDetail, setrichDetail] = useState("");

  // 获取分类名称 一级分类名称或者二级分类名称
  const getCategoryName = async (parentId: string) => {
    // 解构请求结果
    const { data: res } = await reqCategory(parentId);
    // 请求成功的时候
    if (res.status === 0) {
      const categorys = res.data;
      // 调用initOptionLists来初始化options 这个options就是层级选择器的内容

      // 如果是一级分类列表
      if (parentId === "0") {
        initOptionLists(categorys);
      } else {
        //否则就是二级分类列表
        return categorys; // 返回async函数的返回的promise就会成功并且value为categorys
      }
    } else {
      // 请求失败的时候输出错误信息
      console.log(res.msg);
    }
  };

  useEffect(() => {
    // 页面首次加载的时候对层级选择器的一级分类进行加载
    getCategoryName("0");

    //取出路由携带过来的数据
    // 使用useLocation钩子函数获取跳转到这个页面时候携带的参数
    // 如果当前路由没有携带参数进入 就setisUpdate(true);

    if (location.state === null) {
      // 没有参数就是默认添加
      setisUpdate(true);
    } else {
      // 带有参数就是要修改已有商品信息
      const { categoryId, pCategoryId, name, desc, price, detail, imgs } =
        location.state.data;

      // 从路由携带的信息中取出图片数组 这个图片信息会当作props传给picwall照片墙组件使用
      setimgsArr(imgs);
      // 从路由携带的信息中获取富文本信息
      // const [richDetail, setrichDetail] = useState(location.state.data.detail);

      setrichDetail(location.state.data.detail);
      // 用cascader暂存当前所选的商品所处的各级分类
      let cascader: Array<any> = [];
      if (categoryId === "0") {
        // 为0说明是一级分类
        cascader.push(categoryId);
      } else {
        cascader.push(pCategoryId);
        cascader.push(categoryId);
      }

      // 用ref获取到当前页面的Form 然后对每个input框设置当前的值
      // setFieldsValue第一个参数是对应的form.item 第二个参数是要设置的value值
      Formref.current?.setFieldsValue({ pname: name });
      Formref.current?.setFieldsValue({ desc: desc });
      Formref.current?.setFieldsValue({ price: price });
      Formref.current?.setFieldsValue({ category: cascader });
      Formref.current?.setFieldsValue({ detail: detail });
      // Formref.current?.setFieldsValue({ pic: imgs });
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // 初始默认optionLists为空
  const optionLists: any[] | (() => any[]) = [];

  // 初始化层级选择的列表内容 需要一个参数categorys 就是请求的返回结果
  const initOptionLists = async (categorys: any) => {
    // 根据categorys数组生产option数组
    // map遍历会返回新的数组
    const optionsValues = categorys.map((item: any) => ({
      value: item._id,
      label: item.name,
      isLeaf: false,
    }));
    // console.log(StatepCategoryId);
    // 如果是个拥有二级分类商品的更新
    // if (isUpdate && StatepCategoryId !== "0") {
    //   // 获取对应的二级分类列表
    //   const { data } = await reqCategory(StatepCategoryId);
    //   console.log(data);
    // }
    // 更新options
    setOptions(optionsValues);
  };

  // 分类选择器数据存储的状态options
  const [options, setOptions] = React.useState(optionLists);

  // 选择分类变时候的回调  打印出选择分类的内容
  // const onChange = (value: any, selectedOptions: any) => {
  //   console.log(value, selectedOptions);
  // };

  // 用于加载下一级分类内容的回调
  const loadData = async (selectedOptions: any) => {
    const targetOption = selectedOptions[selectedOptions.length - 1];
    // .loading就是加载动画
    targetOption.loading = true;

    // 根据选中的分类 请求这一级的下一级分类列表
    const subcategorys = await getCategoryName(targetOption.value);

    // 二级分类列表有数据的情况下
    if (subcategorys && subcategorys.length > 0) {
      // 生产二级列表的options
      const childrenOptions = subcategorys.map((item: any) => ({
        value: item._id,
        label: item.name,
        isLeaf: true,
      }));
      // 关联到当前选择一级options上
      targetOption.children = childrenOptions;
      // 成功获取二级分类列表或者二级分类列表没内容的时候隐藏loading
      targetOption.loading = false;
    } else {
      // 当前选择的一级分类没有对应的二级分类
      // 这时候就会反过来把一级分类isLeaf设置为true
      targetOption.isLeaf = true;
      // 成功获取二级分类列表或者二级分类列表没内容的时候隐藏loading
      targetOption.loading = false;
    }
    // 更新保存所有层级选择器value的options
    // 因为在上面对一级选择的一级分类的的子分类请求并且加载到一级分类的children上了
    // 第一次选择某个分类的子分类需要加载 会直接修改option 第二次的时候就不需要再请求后台了
    setOptions([...options]);
  };

  // Card的title布置
  const title = (
    <span style={{ color: "blue", fontSize: 20, cursor: "pointer" }}>
      <span
        onClick={() => {
          // navigate(-1)表示goback  navigate(1)表示前进
          navigate(-1);
        }}
      >
        <ArrowLeftOutlined />
      </span>
      <span style={{ marginLeft: 10 }}>添加商品</span>
    </span>
  );

  // 指定form表单里面item布局分布
  const formItemLayout = {
    labelCol: { span: 2 }, // 左侧label的宽度
    wrapperCol: { span: 8 }, // 右侧输入框的宽度
  };

  // 点击提交按钮触发绑定在Form组件上的onFinish方法
  const onFinish = async (values: any) => {
    // 这里调用子组件照片墙ref实例上向外暴露的方法getImagesList 获取到了当前上传中的图片名称数组
    // 并且将获取的照片名称数组给到了values上
    values.imgs = picRef.current?.getImagesList();
    // 名称修改
    values.name = values.pname;

    // 因为用的是一般input 所以获取的价格是字符 这里把价格转成数组再赋值给values
    values.price = parseInt(values.price);
    // 通过调用子组件富文本编辑器上的方法getInputDraft来获取转换后的输入内容
    values.detail = richText.current.getInputDraft();

    // 只有当所有的input上的rules都满足的时候才能读取到values 不然会报出问题的input

    // 名称修改
    const productInfo: any = {};
    productInfo.name = values.name;
    productInfo.desc = values.desc;
    productInfo.imgs = values.imgs;
    productInfo.price = values.price;
    productInfo.detail = values.detail;
    productInfo.categoryId = values.category;
    // productInfo.pCategoryId = values.detail;
    // productInfo._id = values.name

    // 根据选择的分类设置分类的id
    // 主要是看选择的是一级分类还是二级分类
    if (values.category.length === 1) {
      productInfo.pCategoryId = "0";
      productInfo.categoryId = values.category[0];
    } else {
      productInfo.pCategoryId = values.category[0];
      productInfo.categoryId = values.category[1];
    }
    // 如果是更新 要添加_id 属性
    if (!isUpdate) {
      productInfo._id = location.state.data._id;
    }

    // 收集完所有的信息之后 准备发送添加/更新修改请求
    const res = await addOrUpdateProduct(productInfo);
    if (res.data.status === 0) {
      message.success(`${isUpdate ? "添加" : "更新"}商品成功`);
    } else {
      message.error(`${isUpdate ? "添加" : "更新"}商品成功`);
    }
  };

  return (
    // 整体就是card里面套个Form
    <Card title={title}>
      {/* <Form>里面布置多个input就需要放在<Form.Item>内 */}
      <Form ref={Formref} {...formItemLayout} onFinish={onFinish}>
        <Form.Item
          name="pname"
          label="商品名称"
          rules={[
            { required: true, whitespace: true, message: "请输入商品名称!" },
            { min: 2, message: "商品名称最短不能小于2位!" },
            { max: 20, message: "商品名称最长不能大于20位!" },
          ]}
        >
          <Input placeholder="请输入商品名称" />
        </Form.Item>

        <Form.Item
          name="desc"
          label="商品描述"
          rules={[
            { required: true, whitespace: true, message: "请输入商品描述!" },
            { max: 200, message: "最长不能大于200位!" },
          ]}
        >
          {/* Input.TextArea专门输入字符 */}
          <Input.TextArea
            placeholder="请输入商品描述"
            showCount
            maxLength={200}
          />
        </Form.Item>

        <Form.Item
          name="price"
          label="商品价格"
          rules={[
            { required: true, message: "请确认商品价格!" },
            // 上面的都是antd自带的简单表单验证 下面要实现自己自定义的表单内容验证
            // 具体可以看https://ant.design/components/form-cn/#components-form-demo-basic
            // 上例子中注册新用户验证两次输入密码是否一致的案例中就使用了validator和getFieldValue
            {
              //validator为自定义校验 value为这个input输入的值
              validator(_, value) {
                if (Number(value) >= 0) {
                  // 当输入的信息满足验证需求的时候 就返回Promise.resolve()就不会报提示 Form也可以拿到内容
                  return Promise.resolve();
                }
                // 当不满足验证时候 就抛出一个错误 Form也就不能拿到input的内容
                return Promise.reject(new Error("商品价格不能低于0元"));
              },
            },
          ]}
        >
          {/* 这里用的是Input不是InputNumber 所以输入被当作字符串 要转为数字 */}
          <Input
            className="InputNumber"
            type="number"
            placeholder="请输入商品价格"
            addonAfter={"元/人民币"}
            // addonAfter就是在Input的框后面加一个固定后缀
          />
        </Form.Item>

        <Form.Item
          name="category"
          label="商品分类"
          rules={[{ required: true, message: "请确认商品价格!" }]}
        >
          {/* 用层级选择器选择分类 */}
          <Cascader
            placeholder="请选择商品所在分类"
            // 需要显示的列表数据数组
            options={options}
            // 当选择一个分类的时候 加载这个分类下的子分类
            loadData={loadData}
            // 变化时候的回调
            // onChange={onChange}
            changeOnSelect
          />
        </Form.Item>

        <Form.Item name="pic" label="商品图片">
          {/* 需要给一个自定义方法cRef 这是在picwall组件中约束的 */}
          {/* 这里的cRef不是ref 而是自定义的属性 用自定义属性来获取子组件的引用 */}
          {/* 因为函数组件使用ref 本人并不太会 只能这样投机取巧 */}
          <PicWall cRef={picRef} imgs={imgsArr}></PicWall>
          {/* imgs={imgsArr}是当前商品的图片信息 作为props传给picwall组件 */}
        </Form.Item>

        <Form.Item
          name="detail"
          label="商品详情"
          labelCol={{ span: 2 }}
          wrapperCol={{ span: 16 }}
        >
          {/* 富文本编辑器组件 */}
          {/* 富文本上暴露有方法  需要父组件获取并且使用 */}
          <RichTextEditor ref={richText} detail={richDetail}></RichTextEditor>
          {/* <Input placeholder="请输入商品详情" /> */}
        </Form.Item>

        {/* 提交按钮 */}
        <Button
          type="primary"
          htmlType="submit"
          className="submitbutton"
          style={{ marginLeft: 55 }}
        >
          {/* 根据isUpdate来决定按钮上是修改还是添加 */}
          {isUpdate ? "确认添加商品" : "确认修改商品"}
        </Button>
      </Form>
    </Card>
  );
};

export default UpDate;

// 视频p78
